
#include <gfx/enum.h>
#include <gfx/idevice.h>
#include <gfx/imesh.h>
#include <gfx/material.h>
#include <scene/entitynode.h>
#include <scene/renderbucket.h>
#include <assert.h>
#include <stdio.h>


ceRenderBucket::ceRenderBucket()
  : _num(0)
{
  Clear();
}

void ceRenderBucket::Clear()
{
  _renderElements.clear ();
  _transparentRenderElements.clear();
  _unlitRenderElements.clear();
  _numSolid=0;
  _numTransparent=0;
  _numUnlit=0;
  _num=0;

}

void ceRenderBucket::Add(	iEffectTechnique			*technique,
													ceTexturePack					*tp,
													ceMaterialSpec        &material,
													ceSharedShaderBinding	&sharedBindings,
													const ceMatrix4f			&matrix,
													iMesh									*mesh,
													ceEntityNode					*node)
{
  if (material.Transparency)
    {
			_transparentRenderElements[technique][tp].push_back (SpatialGeometry(material, sharedBindings, matrix, mesh, node));
      ++_numTransparent;
    }
  else if (material.Unlit)
    {
			_unlitRenderElements[technique][tp].push_back (SpatialGeometry(material, sharedBindings, matrix, mesh, node));
      ++_numUnlit;
    }
  else
    {
			_renderElements[technique][tp].push_back (SpatialGeometry(material, sharedBindings, matrix, mesh, node));
      ++_numSolid;
    }

  ++_num;
}

unsigned ceRenderBucket::GetNumberOfElements()
{
  return _num;
}

void ceRenderBucket::Debug ()
{
  printf ("Nums: Solid: %d   Transparent: %d     Unlit: %d\n",
          _numSolid,
          _numTransparent,
          _numUnlit);
  fflush(stdout);
}

void ceRenderBucket::Add(ceLightObject *light)
{
  _lights.push_back (light);
}

void ceRenderBucket::Add(iEffectCamera *camera)
{
  _effectCameras.push_back (camera);
}

unsigned ceRenderBucket::GetNumberOfLights() const
{
  return _lights.size ();
}

ceLightObject* ceRenderBucket::GetLight (unsigned light)
{
  assert (light < _lights.size());

  ceLightObject* l = _lights[light];
  return l;
}

void ceRenderBucket::BindLights (iDevice* device, ceEntityNode* entity)
{
  /*
	assert (device);
	assert (entity);
	device->ClearLights ();
	for (unsigned i=0, j=entity->GetNumberOfLights (); i<j; i++)
		{
			device->AddLight (entity->GetLight (i));
		}
  */
}

ceLightObject* ceRenderBucket::BindLight(iDevice *device, unsigned light)
{
	/*
  assert (light < _lights.size());

  ceLightObject* l = _lights[light];
  device->SetLight(l->GetLight());
  return l
	*/
	return 0;
}

unsigned ceRenderBucket::GetNumberOfEffectCameras() const
{
  return _effectCameras.size ();
}

iEffectCamera *ceRenderBucket::GetEffectCamera(unsigned idx)
{
  assert (idx < _effectCameras.size ());

  return _effectCameras[idx];
}

void ceRenderBucket::RenderBoundingBoxes(const ceRenderEnvironment &env, iDevice *device)
{
  EffectTextureGeometryList::iterator sit;
  for (sit = _renderElements.begin (); sit != _renderElements.end (); ++sit)
    {
      iEffectTechnique* effect = sit->first;
			device->SetEffectTechnique(effect);

      device->BeginPass(RP_Diffuse);

      TextureGeometryList& tgl = sit->second;
      TextureGeometryList::iterator tit;
      for (tit = tgl.begin (); tit != tgl.end (); ++tit)
        {
          ceTexturePack* tp = tit->first;
          tp->Bind(RP_Diffuse, device);

          SpatialGeometryList& gl = tit->second;
          SpatialGeometryList::iterator git;
          for (git = gl.begin (); git != gl.end (); ++git)
            {
              SpatialGeometry& sg = *git;
              device->SetMaterial(&sg._material);
              device->SetModelMatrix(sg._matrix);
              device->ApplyShaderStates(&sg._sharedBindings);
              device->DrawBoundingBox(sg._mesh->GetBoundingBox());

            }
        }

      device->EndPass(RP_Diffuse);
    }

}

void ceRenderBucket::Render(EffectTextureGeometryList& list,  const ceRenderEnvironment &env, iDevice *device, ceRenderPass pass, bool renderAll)
{
  EffectTextureGeometryList::iterator eit;
  for (eit = list.begin (); eit != list.end (); ++eit)
    {
      iEffectTechnique* effect = eit->first;
			device->SetEffectTechnique(effect);

      if (!device->BeginPass(pass))
				{
					// materials, that don't support the given render pass
					// must not get rendered.
					continue;
				}

      TextureGeometryList& tgl = eit->second;
      TextureGeometryList::iterator tit;
      for (tit = tgl.begin (); tit != tgl.end (); ++tit)
        {
          ceTexturePack* tp = tit->first;
          tp->Bind(pass, device);

          SpatialGeometryList& gl = tit->second;
          SpatialGeometryList::iterator git;
          for (git = gl.begin (); git != gl.end (); ++git)
            {
              SpatialGeometry& sg = *git;
              if (renderAll || sg._node->ShouldRender ())
                {
                  device->SetMaterial(&sg._material);
                  device->SetModelMatrix(sg._matrix);
									device->ApplyShaderStates(&sg._sharedBindings);
                  sg._mesh->Draw(env, device);
                }
            }
        }

      device->EndPass(pass);
    }
}

void ceRenderBucket::RenderSolid(const ceRenderEnvironment& env, iDevice *device, ceRenderPass pass, bool renderAll)
{
  Render (_renderElements, env, device, pass, renderAll);
}

void ceRenderBucket::RenderTransparent(const ceRenderEnvironment& env, iDevice *device, ceRenderPass pass, bool renderAll)
{
  Render (_transparentRenderElements, env, device, pass, renderAll);
}

void ceRenderBucket::RenderUnlit(const ceRenderEnvironment& env, iDevice *device, ceRenderPass pass, bool renderAll)
{
  Render (_unlitRenderElements, env, device, pass, renderAll);
}

void ceRenderBucket::Render(const ceRenderEnvironment& env, iDevice *device, ceRenderPass pass, bool renderAll)
{
  Render (_unlitRenderElements, env, device, pass, renderAll);
  Render (_renderElements, env, device, pass, renderAll);
  Render (_transparentRenderElements, env, device, pass, renderAll);
}
